package com.ddc.study.feature.query;

import android.content.Context;

import com.ddc.study.entity.Article;
import com.ddc.study.entity.History;
import com.ddc.study.entity.HotKey;
import com.ddc.study.entity.MainData;
import com.ddc.study.net.NetworkUtil;
import com.ddc.study.net.response.BaseResponse;
import com.ddc.study.util.Log;

import java.util.List;

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModel;
import androidx.paging.DataSource;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PageKeyedDataSource;
import androidx.paging.PagedList;
import androidx.room.Room;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

/**
 * Created on 2019/11/16.
 *
 * @author lshun
 * @version 1.0
 */
public class QueryViewModel extends ViewModel {
    private static final String TAG = "QueryViewModel";

    private static final String KEY_SHOW_QUERY_RESULT = "showQueryResult";

    @NonNull
    private MutableLiveData<Boolean> mShowQueryResult;

    @NonNull
    private MutableLiveData<List<HotKey>> mHotKeyLiveData;

    private String mQueryWord;

    private LiveData<PagedList<History>> mQueryHistoryPagedListLiveData;

    private LiveData<PagedList<Article>> mQueryResultPagedListLiveData;

    public QueryViewModel(SavedStateHandle savedStateHandle) {
        if (savedStateHandle != null && savedStateHandle.contains(KEY_SHOW_QUERY_RESULT)) {
            mShowQueryResult = savedStateHandle.getLiveData(KEY_SHOW_QUERY_RESULT);
        } else {
            mShowQueryResult = new MutableLiveData<>(false);
        }

        mHotKeyLiveData = new MutableLiveData<>();
        NetworkUtil.getService().getHotKey().enqueue(new Callback<BaseResponse<List<HotKey>>>() {
            @Override
            public void onResponse(@NonNull Call<BaseResponse<List<HotKey>>> call,
                                   @NonNull Response<BaseResponse<List<HotKey>>> response) {
                if (response.isSuccessful()) {
                    BaseResponse<List<HotKey>> body = response.body();
                    if (body == null) {
                        onFailure(call, new Exception("No body"));
                    } else {
                        if (body.isSuccess()) {
                            List<HotKey> data = body.getData();
                            if (data == null) {
                                onFailure(call, new Exception("Server Error, No data field"));
                            } else {
                                mHotKeyLiveData.setValue(data);
                            }
                        } else {
                            onFailure(call, new Exception(body.getErrorMsg()));
                        }
                    }
                } else {
                    onFailure(call, new Exception("Request Failed" + response.code()));
                }
            }

            @Override
            public void onFailure(@NonNull Call<BaseResponse<List<HotKey>>> call,
                                  @NonNull Throwable t) {
                Log.e(TAG, "load hot key data failed", t);
            }
        });
    }

    public boolean isShowQueryResult() {
        Boolean b = mShowQueryResult.getValue();
        return b != null && b;
    }

    public void setShowQueryResult(boolean showQueryResult) {
        mShowQueryResult.setValue(showQueryResult);
    }

    @NonNull
    public MutableLiveData<List<HotKey>> getHotKeyLiveData() {
        return mHotKeyLiveData;
    }

    public void setQueryWord(@NonNull String queryWord) {
        mQueryWord = queryWord;
    }

    @NonNull
    public LiveData<PagedList<History>> getQueryHistoryPagedListLiveData(@NonNull Context context) {
        if (mQueryHistoryPagedListLiveData == null) {
            AppDatabase appDatabase = Room.databaseBuilder(
                    context.getApplicationContext(), AppDatabase.class, "db")
                    .build();
            DataSource.Factory<Integer, History> factory = appDatabase.historyDao().getAllData();
            mQueryHistoryPagedListLiveData = new LivePagedListBuilder<>(factory, 50).build();
        }

        return mQueryHistoryPagedListLiveData;
    }

    @NonNull
    public LiveData<PagedList<Article>> getQueryResultPagedListLiveData(@NonNull String keyword) {
        if (mQueryResultPagedListLiveData == null) {
            PagedList.Config config = new PagedList.Config.Builder()
                    .setEnablePlaceholders(true)
                    .setInitialLoadSizeHint(20)
                    .setPageSize(20)
                    .setPrefetchDistance(5)
                    .build();

            mQueryResultPagedListLiveData = new LivePagedListBuilder<>(new PageKeyedDataSource.Factory<Integer, Article>() {
                @NonNull
                @Override
                public DataSource<Integer, Article> create() {
                    return new PageKeyedDataSource<Integer, Article>() {
                        private static final String TAG = "QueryViewModel$DataSourceFactory";

                        @Override
                        public void loadInitial(@NonNull LoadInitialParams<Integer> params,
                                                @NonNull LoadInitialCallback<Integer, Article> callback) {
                            Log.e(TAG, "loadInitial -> requestedLoadSize = " + params.requestedLoadSize
                                    + ", placeholdersEnabled = " + params.placeholdersEnabled
                                    + ", thread = " + Thread.currentThread().getName());

                            NetworkUtil.getCookieService()
                                    .queryArticle(0, keyword)
                                    .enqueue(new Callback<BaseResponse<MainData<Article>>>() {
                                        @Override
                                        public void onResponse(@NonNull Call<BaseResponse<MainData<Article>>> call,
                                                               @NonNull Response<BaseResponse<MainData<Article>>> response) {
                                            if (response.isSuccessful()) {
                                                BaseResponse<MainData<Article>> body = response.body();
                                                if (body == null) {
                                                    onFailure(call, new Exception("No body"));
                                                } else {
                                                    if (body.isSuccess()) {
                                                        MainData<Article> data = body.getData();
                                                        if (data == null) {
                                                            onFailure(call, new Exception("Server Error, No data field"));
                                                        } else {
                                                            callback.onResult(data.getData(), null, data.isDataLoadOver() ? null : 1);
                                                        }
                                                    } else {
                                                        onFailure(call, new Exception(body.getErrorMsg()));
                                                    }
                                                }
                                            } else {
                                                onFailure(call, new Exception("Request Failed" + response.code()));
                                            }
                                        }

                                        @Override
                                        public void onFailure(@NonNull Call<BaseResponse<MainData<Article>>> call,
                                                              @NonNull Throwable t) {
                                            //callback.onResult(new ArrayList<>(), null, 2);
                                        }
                                    });
                        }

                        @Override
                        public void loadBefore(@NonNull LoadParams<Integer> params,
                                               @NonNull LoadCallback<Integer, Article> callback) {
                            Log.e(TAG, "loadBefore -> key = " + params.key
                                    + ", requestedLoadSize = " + params.requestedLoadSize
                                    + ", thread = " + Thread.currentThread().getName());

                            NetworkUtil.getCookieService()
                                    .queryArticle(params.key - 1, keyword)
                                    .enqueue(new Callback<BaseResponse<MainData<Article>>>() {
                                        @Override
                                        public void onResponse(@NonNull Call<BaseResponse<MainData<Article>>> call,
                                                               @NonNull Response<BaseResponse<MainData<Article>>> response) {
                                            if (response.isSuccessful()) {
                                                BaseResponse<MainData<Article>> body = response.body();
                                                if (body == null) {
                                                    onFailure(call, new Exception("No body"));
                                                } else {
                                                    if (body.isSuccess()) {
                                                        MainData<Article> data = body.getData();
                                                        if (data == null) {
                                                            onFailure(call, new Exception("Server Error, No data field"));
                                                        } else {
                                                            callback.onResult(data.getData(), params.key > 1 ? params.key - 1 : null);
                                                        }
                                                    } else {
                                                        onFailure(call, new Exception(body.getErrorMsg()));
                                                    }
                                                }
                                            } else {
                                                onFailure(call, new Exception("Request Failed" + response.code()));
                                            }
                                        }

                                        @Override
                                        public void onFailure(@NonNull Call<BaseResponse<MainData<Article>>> call,
                                                              @NonNull Throwable t) {
                                            //callback.onResult(new ArrayList<>(), null);
                                        }
                                    });
                        }

                        @Override
                        public void loadAfter(@NonNull LoadParams<Integer> params,
                                              @NonNull LoadCallback<Integer, Article> callback) {
                            Log.e(TAG, "loadAfter -> key = " + params.key
                                    + ", requestedLoadSize = " + params.requestedLoadSize
                                    + ", thread = " + Thread.currentThread().getName());

                            NetworkUtil.getCookieService()
                                    .queryArticle(params.key, keyword)
                                    .enqueue(new Callback<BaseResponse<MainData<Article>>>() {
                                        @Override
                                        public void onResponse(@NonNull Call<BaseResponse<MainData<Article>>> call,
                                                               @NonNull Response<BaseResponse<MainData<Article>>> response) {
                                            if (response.isSuccessful()) {
                                                BaseResponse<MainData<Article>> body = response.body();
                                                if (body == null) {
                                                    onFailure(call, new Exception("No body"));
                                                } else {
                                                    if (body.isSuccess()) {
                                                        MainData<Article> data = body.getData();
                                                        if (data == null) {
                                                            onFailure(call, new Exception("Server Error, No data field"));
                                                        } else {
                                                            callback.onResult(data.getData(), data.isDataLoadOver() ? null : params.key + 1);
                                                        }
                                                    } else {
                                                        onFailure(call, new Exception(body.getErrorMsg()));
                                                    }
                                                }
                                            } else {
                                                onFailure(call, new Exception("Request Failed" + response.code()));
                                            }
                                        }

                                        @Override
                                        public void onFailure(@NonNull Call<BaseResponse<MainData<Article>>> call,
                                                              @NonNull Throwable t) {
                                            //callback.onResult(new ArrayList<>(), null);
                                        }
                                    });
                        }

                    };
                }
            }, config).build();
        }

        return mQueryResultPagedListLiveData;
    }
}
